# gReloaded is used to determine when a python file reset has occured.
# After a python reaload, any time the screen tries to update, it will
# kill the screen. This is done because many times a screen will
# still be up and trying to update, which will cause problems because
# often global variables have not been reinitialized yet. Afterwards,
# gReloaded is set back to false.
#
gReloaded = True

import CvUtil
from CvPythonExtensions import *

import PlanetfallSpellsInterface

ArtFileMgr = CyArtFileMgr()
localtext = CyTranslator()
gc = CyGlobalContext()
import time

ENTITY_TABLE_CONTROL_NAME = "EntityTableControl"



class EntityRow:
        """
        This is a little helper class that is passed to the render functions. It allows for very simple creation
        of the entities by making all the calls become a bunch of functions all with the same arguments.
        """
        
        def __init__(self, iRowWidth, iRowHeight):
                self.pWidgets = []
                self.iRowWidth = iRowWidth
                self.iRowHeight = iRowHeight

        def getWidgets(self):
                """
                Returns the widgets that have been added.
                """
                return self.pWidgets

        def addWidget(self, pWidget):
                """
                Adds a widget to the row. Gives it it's unique identifying widget number too.
                """
                if not isinstance(pWidget, Widget):
                        raise Exception, "Passed a non-widget object to addWidget"
                pWidget.ITEM_NUMBER = len(self.pWidgets)
                self.pWidgets.append(pWidget)

        def getWidth(self): return self.iRowWidth
        def getHeight(self): return self.iRowHeight

        def getPercentWidth(self, iPercent): return (self.iRowWidth * iPercent) / 100
        def getPercentHeight(self, iPercent): return (self.iRowHeight * iPercent) / 100

        """
        Factory Methods
        """
        
        def createTextWidget(self):
                """
                Returns a new TextWidget widget.
                """
                return TextWidget()

        def createImageWidget(self):
                """
                Returns a new ImageWidget widget.
                """
                return ImageWidget()

class Widget:
        """
        This is a little helper struct that is used to collect the widgets the person wants displayed on the entity-select screen.
        """
                

        def __init__(self):
                self.fX = 0
                self.fY = 0

                # This is set by the entity row when the widget is saved into the row.
                # It specifies what spot in the list it is.
                self.ITEM_NUMBER = -1
                
                # This help text is lazy loaded if none.
                # This means that the first time getHelpText is called, the callback functions
                # are used to get the string, and this variable is filled.
                self.szHelpText = None

                # These are the callbacks to create the help text.
                self.pHelpTextCallback = None
                self.pHelpTextArguments = ()

        def getX(self): return self.fX
        def setX(self, fX): self.fX = fX

        def getY(self): return self.fY
        def setY(self, fY): self.fY = fY

        def getHelpText(self):
                """
                Gets the help text. This might require calling a callback using lazy loading.
                """

                # Check to see if the string is already set. If so, return it.
                if (self.szHelpText is not None):
                        return self.szHelpText

                # Check to see if there is no callback function.
                # If there isn't, just return right now.
                if (self.pHelpTextCallback is None):
                        return ""

                self.szHelpText = apply(self.pHelpTextCallback, self.pHelpTextArguments)
                
                        


                return self.szHelpText

        def setHelpText(self, szText):
                """
                This will set the help text to a string value.
                Note that this text will override any text determined from the callback method.
                """
                self.szHelpText = szText
        
        def getHelpTextCallback(self): return self.pHelpTextCallback
        def getHelpTextCallbackArgs(self): return self.pHelpTextArguments
        def setHelpTextCallback(self, pHelpTextCallback, pArgs):
                """
                Sets the callback function and arguments.
                The callback should be a function, and the arguments should be a list or None.
                """
                if not ( (pArgs is None) or isinstance(pArgs, list) or isinstance(pArgs, tuple) ):
                        raise Exception, "Args must be a list, tuple or None"

                self.pHelpTextCallback = pHelpTextCallback

                # We don't want to be passing None using apply, so we'll make it an empty tuple if necessary.
                if pArgs is None:
                        self.pHelpTextArguments = ()
                else:
                        self.pHelpTextArguments = pArgs

class TextWidget(Widget):
        """
        Used to display text on the screen.
        """
        def __init__(self):
                Widget.__init__(self)

                self.szText = ""

        def getText(self): return self.szText
        def setText(self, szText): self.szText = unicode(szText)

class ImageWidget(Widget):
        """
        Used to display an image on the screen.
        """
        def __init__(self):
                Widget.__init__(self)

                self.fWidth = 0
                self.fHeight = 0
                self.szImage = ""

        def getWidth(self): return self.fWidth
        def setWidth(self, fWidth): self.fWidth = fWidth

        def getHeight(self): return self.fHeight
        def setHeight(self, fHeight): self.fHeight = fHeight

        def getImage(self): return self.szImage
        def setImage(self, szImage): self.szImage = szImage

class PlanetfallSpellsEntitySelectScreen:
	def __init__(self):
		self.X_MAIN_PANEL = 0
		self.Y_MAIN_PANEL = 0
		self.W_MAIN_PANEL = 0
		self.H_MAIN_PANEL = 0

		self.X_SCROLL_PANEL = 0
		self.Y_SCROLL_PANEL = 0
		self.W_SCROLL_PANEL = 0
		self.H_SCROLL_PANEL = 0

		self.ENTITY_ROW_WIDTH = 0
		self.ENTITY_ROW_HEIGHT = 0

		self.pRenderCallback = None
		self.pCastingGroup = None
		self.pCastingPlot = None
		self.pTargetList = None
                self.pWidgets = None
                
		self.iHighlighted = -1

		self.pWidgetRenderFunctions = { TextWidget : self.renderTextWidget ,
                                                ImageWidget : self.renderImageWidget
                                                }

		self.CONFIRM_BUTTON_FUNCTION = "CONFIRM"
		self.CANCEL_BUTTON_FUNCTION = "CANCEL"




		self.m_bCancelOnNextUpdate = False

        def getScreen(self):
                return CyGInterfaceScreen(PlanetfallSpellsInterface.getEntitySelectScreenName(), PlanetfallSpellsInterface.getEntitySelectScreenEnum() )

        def setDimensions(self, screen):
                xRes = screen.getXResolution()
                yRes = screen.getYResolution()

                X_CENTER = xRes / 2
                Y_CENTER = yRes / 2

                # Makes the main panel width and height be in relation to the entire screen.
                self.W_MAIN_PANEL = xRes * 3 / 5
                self.H_MAIN_PANEL = yRes * 3 / 5

                # Centers main the panel
                self.X_MAIN_PANEL = (X_CENTER) - (self.W_MAIN_PANEL / 2)
                self.Y_MAIN_PANEL = (Y_CENTER) - (self.H_MAIN_PANEL / 2)

                # Makes the scroll panel about 2/3 the size of the entire panel.
                self.W_SCROLL_PANEL = (self.W_MAIN_PANEL * 5) / 6
                self.H_SCROLL_PANEL = (self.H_MAIN_PANEL * 2) / 3

                # Centers the scroll panel
                self.X_SCROLL_PANEL = (X_CENTER) - (self.W_SCROLL_PANEL / 2)
                self.Y_SCROLL_PANEL = (Y_CENTER) - ((self.H_SCROLL_PANEL * 3) / 5)

                # Makes the height and width fo the rows in relation to the size of the scroll panel.
                self.ENTITY_ROW_WIDTH = (self.W_SCROLL_PANEL * 14) / 15
                self.ENTITY_ROW_HEIGHT = self.H_SCROLL_PANEL / 5

                self.W_CONFIRM_CANCEL_BUTTON = 100
                self.H_CONFIRM_CANCEL_BUTTON = 40

                self.X_CONFIRM_BUTTON = (X_CENTER) - (self.W_SCROLL_PANEL / 3) - (self.W_CONFIRM_CANCEL_BUTTON / 2)# - (self.W_CONFIRM_CANCEL_BUTTON)
                self.X_CANCEL_BUTTON = (X_CENTER) + (self.W_SCROLL_PANEL / 3) - (self.W_CONFIRM_CANCEL_BUTTON / 2)#(self.W_SCROLL_PANEL / 3) - (self.W_CONFIRM_CANCEL_BUTTON)
                self.Y_CONFIRM_CANCEL_BUTTON = Y_CENTER + (((self.H_MAIN_PANEL / 2) * 3) / 4)

                self.BACKGROUND_BUTTON_ITEM = -1

        def getEntityRowName(self, iRow):
                return "Row-%d" % iRow

        def getWidgetName(self, iRow, iItem):
                return "Widget-%d-%d" % (iRow, iItem)


	def interfaceScreen(self, pCastingGroup, pCastingPlot, pTargetList, pRenderCallback):
                global gReloaded
                gReloaded = False
                
                self.pCastingGroup = pCastingGroup
                self.pCastingPlot = pCastingPlot
                self.pTargetList = pTargetList
		self.pRenderCallback = pRenderCallback

		if (pRenderCallback == None):
                        raise Exception, "Render callback cannot be null."

		self.iHighlighted = -1

                ##################################################################
		# Create screen
                ##################################################################
		
		screen = self.getScreen()

		self.setDimensions(screen)
		screen.setDimensions(0, 0, screen.getXResolution(), screen.getYResolution())

		##################################################################
		# Generate the widget list to be displayed from the spell's class.
                ##################################################################
		self.generateWidgetList(self.pTargetList)
		
                ##################################################################
		# Create panels
                ##################################################################
		
		# Main
		szMainPanel = "MainPanel"

                # Scroll Panel
		szScrollPanel = "ScrollPanel"
		screen.addPanel( szMainPanel, "Choose a target", "", true, true,
			self.X_MAIN_PANEL, self.Y_MAIN_PANEL, self.W_MAIN_PANEL, self.H_MAIN_PANEL, PanelStyles.PANEL_STYLE_EXTERNAL )


                #screen.addPanel( "ScrollPanelBackground", "", "", true, true,
		#	self.X_SCROLL_PANEL, self.Y_SCROLL_PANEL - (self.Y_SCROLL_PANEL / 20), self.W_SCROLL_PANEL - (self.W_SCROLL_PANEL / 70), self.H_SCROLL_PANEL + (self.H_SCROLL_PANEL / 5), PanelStyles.PANEL_STYLE_STANDARD)
		screen.addScrollPanel(szScrollPanel, "", self.X_SCROLL_PANEL, self.Y_SCROLL_PANEL, self.W_SCROLL_PANEL, self.H_SCROLL_PANEL, PanelStyles.PANEL_STYLE_STANDARD)

                # Create each row.
                iRow = 0
		for pTarget in self.pTargetList:
                        self.renderEntityRow(screen, iRow)

                        iRow += 1

                # Create "Confirm" button.
                screen.setButtonGFC(self.CONFIRM_BUTTON_FUNCTION, localtext.getText("TXT_KEY_SPELL_TARGET_CONFIRM", ()), "",
                                                                           self.X_CONFIRM_BUTTON, self.Y_CONFIRM_CANCEL_BUTTON,
                                                                           self.W_CONFIRM_CANCEL_BUTTON, self.H_CONFIRM_CANCEL_BUTTON,
                                                                           WidgetTypes.WIDGET_GENERAL, -1, -1, ButtonStyles.BUTTON_STYLE_STANDARD)

                # Create the "Cancel" button.
                screen.setButtonGFC(self.CANCEL_BUTTON_FUNCTION, localtext.getText("TXT_KEY_SPELL_TARGET_CANCEL", ()), "",
                                                                           self.X_CANCEL_BUTTON, self.Y_CONFIRM_CANCEL_BUTTON,
                                                                           self.W_CONFIRM_CANCEL_BUTTON, self.H_CONFIRM_CANCEL_BUTTON,
                                                                           WidgetTypes.WIDGET_GENERAL, -1, -1, ButtonStyles.BUTTON_STYLE_STANDARD)

                self.m_bCancelOnNextUpdate = False

                screen.showScreen(PopupStates.POPUPSTATE_QUEUED, False)

        def generateWidgetList(self, pTargets):
                # Reinitialize the structure, clearing out any old widgets.
                self.pWidgets = [None] * len(pTargets)

                # Loop through all the targets...
                iRow = 0
                for pTarget in pTargets:
                        # Pass a new entity row object to the renderer function.
                        pEntityRow = EntityRow(self.ENTITY_ROW_WIDTH, self.ENTITY_ROW_HEIGHT)
                        self.pRenderCallback(pEntityRow, pTargets[iRow])

                        # Save the widgets into it's spot in the widgets list.
                        self.pWidgets[iRow] = pEntityRow.getWidgets()
                        iRow += 1
                        

        def renderEntityRow(self, pScreen, iRow):
                szPanelAttachName = self.getEntityRowName(iRow)

                self.renderPanel(pScreen, iRow)
                        
                print type(self.pWidgets[iRow])
                for pWidget in self.pWidgets[iRow]:
                        # Renders the widget depending on what type of widget it is.
                        self.pWidgetRenderFunctions[(pWidget.__class__)](pScreen, pWidget, iRow)

                # Place a large button over the row to capture clicks.
                szInvisiButton = "InvisiButton" + str(iRow)
                pScreen.setImageButtonAt(szInvisiButton, szPanelAttachName, "" , 0, 0, self.ENTITY_ROW_WIDTH, self.ENTITY_ROW_HEIGHT, WidgetTypes.WIDGET_PLANETFALL_SPELL_TARGET_INFO, iRow, self.BACKGROUND_BUTTON_ITEM)
                pScreen.moveToBack(szInvisiButton)

        def renderPanel(self, pScreen, iRow):
                szPanelName = self.getEntityRowName(iRow)
                if self.iHighlighted == iRow:
                        pScreen.attachPanelAt("ScrollPanel", szPanelName, "", "", False, False, PanelStyles.PANEL_STYLE_CITY_TANSHADE,
                                      0, iRow * (self.ENTITY_ROW_HEIGHT), self.ENTITY_ROW_WIDTH, self.ENTITY_ROW_HEIGHT, WidgetTypes.WIDGET_GENERAL, -1, -1)
                else:
                        pScreen.attachPanelAt("ScrollPanel", szPanelName, "", "", False, False, PanelStyles.PANEL_STYLE_OUT,
                                      0, iRow * (self.ENTITY_ROW_HEIGHT), self.ENTITY_ROW_WIDTH, self.ENTITY_ROW_HEIGHT, WidgetTypes.WIDGET_GENERAL, -1, -1)

                        
                

        def unrenderPanel(self, pScreen, iRow):
                szPanelName = self.getEntityRowName(iRow)
                pScreen.deleteWidget(szPanelName)


        def getEntityHelpText(self, iRow, iTarget):
                if (iTarget <> self.BACKGROUND_BUTTON_ITEM):
                        # Prevents help text from being called when the screen is being brought down.
                        if ((self.pWidgets is not None) and (self.pWidgets[iRow] is not None) and (self.pWidgets[iRow][iTarget] is not None)):
                                return unicode(self.pWidgets[iRow][iTarget].getHelpText())
                
        def executeTargetInfo(self, iRow, iTarget):
                self.selectEntityRow(iRow)

        def selectEntityRow(self, iRow):
                # Unselect the old row
                pScreen = self.getScreen()
                if (self.iHighlighted != iRow):
                        # If another row needs to be set back to unselected...
                        if (self.iHighlighted >= 0):
                                iOldHighlighted = self.iHighlighted
                                self.unrenderPanel(pScreen, iOldHighlighted)
                                self.iHighlighted = iRow
                                self.renderEntityRow(pScreen, iOldHighlighted)
                        else:
                                self.iHighlighted = iRow

                        self.renderEntityRow(pScreen, iRow)

                # Sometimes we'll lose focus while drawing widgets, making it unable to escape out of the
                # screen with the escape key without first clicking the background.
                pScreen.setFocus("MainPanel")
                

        ###########################################################
        # Helper functions to convert different positioning systems
        # and also positioning the units.
        ###########################################################
        # TODO: DEPRACATED
        def offset(self, pWidgetArgs, iRow):
                dX, dY, dWidth, dHeight, pArgs = pWidgetArgs
                return (dX,
                        dY,
                        dWidth,
                        dHeight,
                        pArgs
                        )
        # TODO: DEPRACATED
        def offsetPercent(self, pWidgetArgs, iRow):
                dX, dY, dWidth, dHeight, szHelpText, pArgs = pWidgetArgs
                return (dX,
                        dY,
                        (dWidth * self.ENTITY_ROW_WIDTH) / 100.0,
                        (dHeight * self.ENTITY_ROW_HEIGHT) / 100.0,
                        pArgs
                        )

        ############################
        # Widget Rendering Functions
        ############################
        def renderImageWidget(self, pScreen, pWidget, iRow):
                szPanelAttachName = self.getEntityRowName(iRow)
                pScreen.setImageButtonAt(self.getWidgetName(iRow, pWidget.ITEM_NUMBER), szPanelAttachName, pWidget.getImage(), pWidget.getX(), pWidget.getY(), pWidget.getWidth(), pWidget.getHeight(),
                                   WidgetTypes.WIDGET_PLANETFALL_SPELL_TARGET_INFO, iRow, pWidget.ITEM_NUMBER)

        def renderTextWidget(self, pScreen, pWidget, iRow):
                szPanelAttachName = self.getEntityRowName(iRow)
                pScreen.setTextAt(self.getWidgetName(iRow, pWidget.ITEM_NUMBER), szPanelAttachName, pWidget.getText(), 0, pWidget.getX(), pWidget.getY(), 0,
                                  FontTypes.GAME_FONT, WidgetTypes.WIDGET_PLANETFALL_SPELL_TARGET_INFO, iRow, pWidget.ITEM_NUMBER)
                
        ##########################
        # Typical screen functions
        ##########################

        def hideScreen(self):
                self.cleanup()
                pScreen = self.getScreen()
                pScreen.hideScreen()
	
	def handleInput(self, inputClass):
                if (not self.m_bCancelOnNextUpdate):
                    # They pressed escape, so cancel the spell casting
                    if (inputClass.getNotifyCode() == int(NotifyCode.NOTIFY_CHARACTER) and
                        inputClass.getData() == int(InputTypes.KB_ESCAPE)):
                        PlanetfallSpellsInterface.cancelSpell( self.pCastingGroup, self.pCastingPlot )

                # Check for pressing of the cancel button
                if (inputClass.getNotifyCode() == int(NotifyCode.NOTIFY_CLICKED)):
                        if (inputClass.getFunctionName() == self.CANCEL_BUTTON_FUNCTION):
                                PlanetfallSpellsInterface.cancelSpell(self.pCastingGroup, self.pCastingPlot)

                # Check if something is highlighted, and if it is, see if they pressed the confirm button.
                if (self.iHighlighted > -1):
                        if (inputClass.getNotifyCode() == int(NotifyCode.NOTIFY_CLICKED)):
                            if (inputClass.getFunctionName() == self.CONFIRM_BUTTON_FUNCTION):
                                    PlanetfallSpellsInterface.onEntitySelect(self.pCastingGroup, self.pCastingPlot, self.pTargetList[self.iHighlighted])

                

	def update(self, fDelta):
                # Closes screens that may have been running during a python
                # file reload.
                global gReloaded
                if (gReloaded):
                    screenToKill = self.getScreen()
                    screenToKill.hideScreen()
                    gReloaded = False
                    return

                if self.m_bCancelOnNextUpdate:
                    self.hideScreen()
                    self.m_bCancelOnNextUpdate = False

        def cleanup(self):
                self.pRenderCallback = None
		self.pCastingGroup = None
		self.pCastingPlot = None
		self.pTargetList = None
                self.pWidgets = None
                
		self.iHighlighted = -1
